import { ref } from 'vue';

// 旋转映射顺序 -- 内容为奖品数组的位置
// 如果九宫格的奖品渲染不通过样式对位置进行修改
// 要保持顺时针的旋转顺序请使用该映射数组
const ROTATE = [0, 1, 2, 4, 7, 6, 5, 3];

/**
 * 根据奖品id从奖品数组获取奖品位置
 * @param {Array} arr 奖品数组
 * @param {String | Number} target 奖品拥有的唯一值
 * @param {String} target 奖品拥有的唯一值的字段名
 * @returns 奖品数组的下标，用于与旋转顺序对应
 */
const findPrizeIndexById = (arr, target, name) => {
  const _idxs = arr.reduce((all, item, index) => {
    if (item[name] === target) {
      all.push(index);
    }
    return all;
  }, []);
  // 避免在有重复的奖品数组取固定位置
  if (_idxs.length) {
    // 向下取整
    const _random = Math.floor(Math.random() * _idxs.length);
    return _idxs[_random];
  } else {
    throw new Error(`Can not found the prize with ${name}('${target}').`);
  }
};

/**
 * 通过控制父节点的样式实现动画效果
 * @param {Object} options
 * {loop, crossX, lowest, len, rotate}
 * 圈数，起始速度，最高速度，奖品数量，旋转顺序映射
 * @returns {Object}
 */
export default function useLottery(options) {
  const { rotate = ROTATE, startClassName = 'start' } = options; // 旋转顺序
  const activedClassName = ref('');
  let prizes = [];
  let lock = false;
  // 重置样式
  const reset = () => {
    activedClassName.value = '';
  };
  // 修改样式
  const cover = (name) => {
    activedClassName.value = name;
  };

  const draw = (time, name) => {
    window.setTimeout(() => {
      cover(name);
    }, time);
  };

  // 更新内部的prizes，在自定义build的时候必须调用
  const update = (arr) => {
    prizes = arr;
  };
  /**
   * 开始函数
   * @param {String | Number} winner 奖品id
   * @param {Function} callback 计算奖品的位置，接收prizes、winner，必须返回中奖奖品的数组下标
   * @returns {Object} {time, prize} 动画的结束时间，抽中的奖品信息
   */
  const start = (winner = 'none', startOptions = {}) => {
    if (lock) return false;
    reset();
    lock = true;
    const { loop, crossX, lowest, len } = options;
    const { find = findPrizeIndexById, findName = 'id' } = startOptions;
    try {
      // 计算奖品在数组的位置
      // 默认的callback在匹配不到奖品是会抛出错误
      const prizeIndex = find(prizes, winner, findName);
      // winner会被替换成中奖奖品在旋转数组中的位置
      // +1是为了匹配总个数，以便计算动画持续时间
      winner = rotate.indexOf(prizeIndex) + 1;
      // 匹配不到对应的位置抛出错误
      if (winner === 0)
        throw new Error('Can not transform prizeIndex to rotateIndex.');
      // 动画持续总时间
      let _time = 0;
      // 总执行次数
      const count = loop * len + winner;
      // console.log('winner is: ', winner, 'count is: ', count);
      // 创建输入x获得y的抛物线函数
      const getTimeGapY = createParabola(crossX, lowest, count);
      // console.time();
      for (let cursor = 0; cursor < count; cursor++) {
        // 旋转位置
        const position = cursor % len;
        // 计算动画的间隔时间
        _time += getTimeGapY(cursor);
        // 设置定时器
        draw(_time, `${startClassName} actived-${rotate[position]}`);
      }
      // 动画结束后取消锁
      window.setTimeout(() => {
        lock = false;
        cover(
          activedClassName.value.replace(
            new RegExp(`${startClassName}\\s?`),
            ''
          )
        );
      }, _time);
      // console.log(_time);
      return { time: _time, prize: prizes[prizeIndex] };
    } catch (err) {
      // 没通过id找到对应的奖品或其他错误都不执行功能。
      console.error(err);
      lock = false;
      return { time: 0, prize: undefined };
    }
  };

  /**
   * 构建并保存抽奖用的数组
   * @param {Array} arr 奖品数组
   * @param {Function} callback 回调函数，接收arr[index]、img，不返回值时使用默认奖品项代替
   * @param {String} img 谢谢参与图片地址
   * @returns
   */
  const build = (arr, callback, img, options) => {
    const { sort } = options;
    const _ = [];
    for (let i = 0; i < 8; i++) {
      const _item = (callback && callback(arr[i], img)) || {
        logo: img,
        name: '再接再厉',
        // la_id: `none${i}`
        id: 'none'
      };
      _.push(_item);
    }
    prizes = _;
    // 乱序
    if (sort) {
      prizes = sort(prizes);
    } else {
      prizes.sort(() => 0.4 - Math.random());
    }
    // console.log(prizes);
    return prizes;
  };

  return [activedClassName, { reset, cover, start, build, update }];
}

// 抛物线
function createParabola(crossX, lowest, endX) {
  const middle = endX / 2;
  const c = crossX;
  const _b = -2 * middle;
  const a = (lowest - c) / (middle * middle * -1);
  const b = _b * a;
  // console.log('a is: ', a, 'b is: ', b, 'c is: ', c);
  // createParabola(0, 50, 40);
  // createParabola(3, -1, 4);
  return function getY(x) {
    return a * (x * x) + b * x + c;
  };
}

// export function sortPrizes(arr, sortName) {
//   return arr.sort(function (a, b) {
//     // 升序
//     return a[sortName] - b[sortName];
//   });
// }
